package com.ht.config.dataAuth;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.ht.module.sys.service.ISysRoleAuthService;
import com.ht.module.sys.vo.SysRoleAuth.UserAuthVo;
import com.ht.util.UserUtil;
import lombok.SneakyThrows;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

import java.sql.Connection;
import java.util.Map;
import java.util.Properties;


/**
 * <p>
 * sql拦截器
 * </p>
 *
 * @author hejialun
 * @since 2022-04-21
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor {
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    @SneakyThrows
    public Object intercept(Invocation invocation) {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        // 先判断是不是SELECT操作
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        //参数列表
        Object parameterObject = boundSql.getParameterObject();
        //查找参数中包含DataScope类型的参数
        DataScope dataScope = findDataScopeObject(parameterObject);
        //旧的sql
        String oldSQL = boundSql.getSql();
        //定义新的sql
        StringBuilder newSQL = new StringBuilder();
        //需要进行数据权限校验
        if (dataScope != null) {
            //获取数据bean
            ISysRoleAuthService sysRoleAuthService = applicationContext.getBean(ISysRoleAuthService.class);
            //获取数据权限
            UserAuthVo vo = sysRoleAuthService.findByUserId(UserUtil.getUserId());
            //判断部门权限是否为空

            //定义新的sql
            newSQL.append("select * from (").append(oldSQL).append(") temp_data_scope");
            if(ObjectUtil.isNotEmpty(vo.getDeptIds()) && !vo.getDeptIds().isEmpty()){
                //进行权限过滤
                newSQL.append(" where temp_data_scope.")
                        .append(dataScope.getScopeName())
                        .append(" in('").append(CollectionUtil.join(vo.getDeptIds(),"','"))
                        .append("')");

                newSQL.append("or ")
                        .append("temp_data_scope.")
                        .append(dataScope.getScopeUser())
                        .append(" in('").append(CollectionUtil.join(vo.getUserIds(),"','"))
                        .append("')");

            }else{
                //没得数据权限-过滤所有
                newSQL.append(" where 1=0");
            }
            metaObject.setValue("delegate.boundSql.sql", newSQL.toString());
        }
        return invocation.proceed();

    }


    /**
     * 生成拦截对象的代理
     *
     * @param target 目标对象
     * @return 代理对象
     */
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    /**
     * mybatis配置的属性
     *
     * @param properties mybatis配置的属性
     */
    @Override
    public void setProperties(Properties properties) {

    }


    /**
     * @param parameterObj
     * @return com.ht.config.dataAuth.DataScope
     * @Author hejialun
     * @Date 9:36 2022/4/21
     * @Description TODO(查找参数是否包括DataScope对象)
     */
    private DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        }else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }


}
